index.js ➔ isNumber   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
module.exports = function (args, opts) {
2
    if (!opts) opts = {};
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
3
    
4
    var flags = { bools : {}, strings : {}, unknownFn: null };
5
6
    if (typeof opts['unknown'] === 'function') {
7
        flags.unknownFn = opts['unknown'];
8
    }
9
10
    if (typeof opts['boolean'] === 'boolean' && opts['boolean']) {
11
      flags.allBools = true;
12
    } else {
13
      [].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
14
          flags.bools[key] = true;
15
      });
16
    }
17
    
18
    var aliases = {};
19
    Object.keys(opts.alias || {}).forEach(function (key) {
20
        aliases[key] = [].concat(opts.alias[key]);
21
        aliases[key].forEach(function (x) {
22
            aliases[x] = [key].concat(aliases[key].filter(function (y) {
23
                return x !== y;
24
            }));
25
        });
26
    });
27
28
    [].concat(opts.string).filter(Boolean).forEach(function (key) {
29
        flags.strings[key] = true;
30
        if (aliases[key]) {
31
            flags.strings[aliases[key]] = true;
32
        }
33
     });
34
35
    var defaults = opts['default'] || {};
36
    
37
    var argv = { _ : [] };
38
    Object.keys(flags.bools).forEach(function (key) {
39
        setArg(key, defaults[key] === undefined ? false : defaults[key]);
40
    });
41
    
42
    var notFlags = [];
43
44
    if (args.indexOf('--') !== -1) {
45
        notFlags = args.slice(args.indexOf('--')+1);
46
        args = args.slice(0, args.indexOf('--'));
47
    }
48
49
    function argDefined(key, arg) {
50
        return (flags.allBools && /^--[^=]+$/.test(arg)) ||
51
            flags.strings[key] || flags.bools[key] || aliases[key];
52
    }
53
54
    function setArg (key, val, arg) {
55
        if (arg && flags.unknownFn && !argDefined(key, arg)) {
56
            if (flags.unknownFn(arg) === false) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
57
        }
58
59
        var value = !flags.strings[key] && isNumber(val)
60
            ? Number(val) : val
61
        ;
62
        setKey(argv, key.split('.'), value);
63
        
64
        (aliases[key] || []).forEach(function (x) {
65
            setKey(argv, x.split('.'), value);
66
        });
67
    }
68
69
    function setKey (obj, keys, value) {
70
        var o = obj;
71
        keys.slice(0,-1).forEach(function (key) {
72
            if (o[key] === undefined) o[key] = {};
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
73
            o = o[key];
74
        });
75
76
        var key = keys[keys.length - 1];
77
        if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') {
78
            o[key] = value;
79
        }
80
        else if (Array.isArray(o[key])) {
81
            o[key].push(value);
82
        }
83
        else {
84
            o[key] = [ o[key], value ];
85
        }
86
    }
87
    
88
    function aliasIsBoolean(key) {
89
      return aliases[key].some(function (x) {
90
          return flags.bools[x];
91
      });
92
    }
93
94
    for (var i = 0; i < args.length; i++) {
95
        var arg = args[i];
96
        
97
        if (/^--.+=/.test(arg)) {
98
            // Using [\s\S] instead of . because js doesn't support the
99
            // 'dotall' regex modifier. See:
100
            // http://stackoverflow.com/a/1068308/13216
101
            var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
102
            var key = m[1];
103
            var value = m[2];
104
            if (flags.bools[key]) {
105
                value = value !== 'false';
106
            }
107
            setArg(key, value, arg);
108
        }
109
        else if (/^--no-.+/.test(arg)) {
110
            var key = arg.match(/^--no-(.+)/)[1];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable key already seems to be declared on line 102. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
111
            setArg(key, false, arg);
112
        }
113
        else if (/^--.+/.test(arg)) {
114
            var key = arg.match(/^--(.+)/)[1];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable key already seems to be declared on line 102. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
115
            var next = args[i + 1];
116
            if (next !== undefined && !/^-/.test(next)
117
            && !flags.bools[key]
118
            && !flags.allBools
119
            && (aliases[key] ? !aliasIsBoolean(key) : true)) {
120
                setArg(key, next, arg);
121
                i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
122
            }
123
            else if (/^(true|false)$/.test(next)) {
124
                setArg(key, next === 'true', arg);
125
                i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
126
            }
127
            else {
128
                setArg(key, flags.strings[key] ? '' : true, arg);
129
            }
130
        }
131
        else if (/^-[^-]+/.test(arg)) {
132
            var letters = arg.slice(1,-1).split('');
133
            
134
            var broken = false;
135
            for (var j = 0; j < letters.length; j++) {
136
                var next = arg.slice(j+2);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable next already seems to be declared on line 115. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
137
                
138
                if (next === '-') {
139
                    setArg(letters[j], next, arg)
140
                    continue;
141
                }
142
                
143
                if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) {
144
                    setArg(letters[j], next.split('=')[1], arg);
145
                    broken = true;
146
                    break;
147
                }
148
                
149
                if (/[A-Za-z]/.test(letters[j])
150
                && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
151
                    setArg(letters[j], next, arg);
152
                    broken = true;
153
                    break;
154
                }
155
                
156
                if (letters[j+1] && letters[j+1].match(/\W/)) {
157
                    setArg(letters[j], arg.slice(j+2), arg);
158
                    broken = true;
159
                    break;
160
                }
161
                else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
162
                    setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg);
163
                }
164
            }
165
            
166
            var key = arg.slice(-1)[0];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable key already seems to be declared on line 102. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
167
            if (!broken && key !== '-') {
168
                if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
169
                && !flags.bools[key]
170
                && (aliases[key] ? !aliasIsBoolean(key) : true)) {
171
                    setArg(key, args[i+1], arg);
172
                    i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
173
                }
174
                else if (args[i+1] && /true|false/.test(args[i+1])) {
175
                    setArg(key, args[i+1] === 'true', arg);
176
                    i++;
0 ignored issues
show
Complexity Coding Style introduced by
You seem to be assigning a new value to the loop variable i here. Please check if this was indeed your intention. Even if it was, consider using another kind of loop instead.
Loading history...
177
                }
178
                else {
179
                    setArg(key, flags.strings[key] ? '' : true, arg);
180
                }
181
            }
182
        }
183
        else {
184
            if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
185
                argv._.push(
186
                    flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
187
                );
188
            }
189
            if (opts.stopEarly) {
190
                argv._.push.apply(argv._, args.slice(i + 1));
191
                break;
192
            }
193
        }
194
    }
195
    
196
    Object.keys(defaults).forEach(function (key) {
197
        if (!hasKey(argv, key.split('.'))) {
198
            setKey(argv, key.split('.'), defaults[key]);
199
            
200
            (aliases[key] || []).forEach(function (x) {
201
                setKey(argv, x.split('.'), defaults[key]);
202
            });
203
        }
204
    });
205
    
206
    if (opts['--']) {
207
        argv['--'] = new Array();
0 ignored issues
show
Coding Style Best Practice introduced by
Using the Array constructor is generally discouraged. Consider using an array literal instead.
Loading history...
208
        notFlags.forEach(function(key) {
209
            argv['--'].push(key);
210
        });
211
    }
212
    else {
213
        notFlags.forEach(function(key) {
214
            argv._.push(key);
215
        });
216
    }
217
218
    return argv;
219
};
220
221
function hasKey (obj, keys) {
222
    var o = obj;
223
    keys.slice(0,-1).forEach(function (key) {
224
        o = (o[key] || {});
225
    });
226
227
    var key = keys[keys.length - 1];
228
    return key in o;
229
}
230
231
function isNumber (x) {
232
    if (typeof x === 'number') return true;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
233
    if (/^0x[0-9a-f]+$/i.test(x)) return true;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
234
    return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
235
}
236
237